/*
 * Para compilar:
 * gcc cpuGL_v1.c -lm -lglut -lGL -lGLU -ljpeg -o cpugl
*/


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <jpeglib.h>


#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#include <GL/glu.h>
#endif

/*
#define TRUE  (-1)
#define FALSE (0)
*/

#define WINDOWSIZEX 1024
#define WINDOWSIZEY 768

#define BYTE  unsigned char
#define DWORD  unsigned int
#define LONG  int
#define UINT  unsigned int
#define WORD  unsigned short int

#pragma pack(1)

// reserved for future use.
typedef struct
{
    long Code;
    char Description[50];
    char Source[20];
} error_type;
error_type GlobalError;

typedef struct
{
    long    ID;
    double  Mass;
    double   PosX;
    double   PosY;
    double   PosZ;
    double   vX;
    double   vY;
    double   vZ;
    double  fX;
    double  fY;
    double  fZ;
    char    Processed; // reserved for future use.
} particuleData;

typedef struct tagBITMAPFILEHEADER { // bmfh
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER{ // bmih
   DWORD  biSize;
   LONG   biWidth;
   LONG   biHeight;
   WORD   biPlanes;
   WORD   biBitCount;
   DWORD  biCompression;
   DWORD  biSizeImage;
   LONG   biXPelsPerMeter;
   LONG   biYPelsPerMeter;
   DWORD  biClrUsed;
   DWORD  biClrImportant;
} BITMAPINFOHEADER;


particuleData *ArrayHead;
int n; // n=> number of particules
double eps = 0.0;
char img_fmt[4]; //image format e.g.(bmp,jpg)
int animate;

int    iout = 0;          // we need these sequencial numbers to name the result files.
double t = 0.0;        // controls the amount of time steps already processed for the simulation.
double dt = 0.0001;    // indicates the contribution of each scan for the simulation evolution.
double dtout = 0.01;   // indicates the value of the step where a result file has to be generated.
double tout = 0.0;
int    timeSteps = 10; //WARNING!! Don't forget to update this value with the desired time steps amount!

double CalculaNCorpos();
void GenerateResultFile(char* FileName);

// angle of rotation for the camera direction
float angle = 0.0f;

// actual vector representing the camera's direction
float lx=0.0f,lz=-1.0f;

// XZ position of the camera
float x=0.0f, y=0.0f, z=5.0f;

// the key states. These variables will be zero
//when no key is being presses
float deltaAngle = 0.0f;
float deltaMove = 0;
float deltaMove2 = 0;
int xOrigin = -1;

int screen;
int coorX = 0;
int coorY = 0;
int coorZ = 0;

int centerX,centerY;

// width and height of the window
int h,w;

// variables to compute frames per second
int frame;
long time, timebase;
char s[50];

// Constant definitions for Menus
#define ZIN 1
#define ZOUT 2

// Pop up menu identifiers
int zoomMenu, mainMenu, colorMenu;

// color for the particles
float red = 1.0f, blue=1.0f, green=1.0f;

// scale of particles
float scale = 1.0f;

// menu status
int menuFlag = 0;

void changeSize(int ww, int hh) {

    w = ww;
    h = hh;

	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if (h == 0)
		h = 1;

	float ratio =  w * 1.0 / h;

	// Use the Projection Matrix
	glMatrixMode(GL_PROJECTION);

	// Reset Matrix
	glLoadIdentity();

	// Set the viewport to be the entire window
	glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45.0f, ratio, 0.1f, 100.0f);

	// Get Back to the Modelview
	glMatrixMode(GL_MODELVIEW);
}

void drawSnowMan() {

	glScalef(scale, scale, scale);
	glColor3f(1.0f, 1.0f, 1.0f);

// Draw Body
	glTranslatef(0.0f ,0.75f, 0.0f);
	glutSolidSphere(0.75f,20,20);

// Draw Head
	glTranslatef(0.0f, 1.0f, 0.0f);
	glutSolidSphere(0.25f,20,20);

// Draw Eyes
	glPushMatrix();
	glColor3f(0.0f,0.0f,0.0f);
	glTranslatef(0.05f, 0.10f, 0.18f);
	glutSolidSphere(0.05f,10,10);
	glTranslatef(-0.1f, 0.0f, 0.0f);
	glutSolidSphere(0.05f,10,10);
	glPopMatrix();

// Draw Nose
	glColor3f(red, green, blue);
	glRotatef(0.0f,1.0f, 0.0f, 0.0f);
	glutSolidCone(0.08f,0.5f,10,2);

	glColor3f(1.0f, 1.0f, 1.0f);
}

void drawParticle()
{
    glScalef(scale, scale, scale);

    //particulas laranjas
    //glColor3f( 0.95f, 0.207, 0.031f);

    //particulas brancas
    //glColor3f( 1.0f, 1.0f, 1.0f);
    glColor3f(red, green, blue);

    glBegin(GL_POINTS);
	//glColor3f(0.0f, 0.0f, 0.0f);

	int i;

	for(i=0;i<n;i++)
	{
        glVertex3f(ArrayHead[i].PosX,ArrayHead[i].PosY,ArrayHead[i].PosZ);
	}

    glEnd();
}

void renderBitmapString(
		float x,
		float y,
		float z,
		void *font,
		char *string) {

	char *c;
	glRasterPos3f(x, y,z);
	for (c=string; *c != '\0'; c++) {
		glutBitmapCharacter(font, *c);
	}
}

void renderStrokeFontString(
		float x,
		float y,
		float z,
		void *font,
		char *string) {

	char *c;
	glPushMatrix();
	glTranslatef(x, y,z);
	glScalef(0.002f, 0.002f, 0.002f);
	for (c=string; *c != '\0'; c++) {
		glutStrokeCharacter(font, *c);
	}
	glPopMatrix();
}

void restorePerspectiveProjection() {

	glMatrixMode(GL_PROJECTION);
	// restore previous projection matrix
	glPopMatrix();

	// get back to modelview mode
	glMatrixMode(GL_MODELVIEW);
}

void setOrthographicProjection() {

	// switch to projection mode
	glMatrixMode(GL_PROJECTION);

	// save previous matrix which contains the
	//settings for the perspective projection
	glPushMatrix();

	// reset matrix
	glLoadIdentity();

	// set a 2D orthographic projection
	gluOrtho2D(0, w, h, 0);

	// switch back to modelview mode
	glMatrixMode(GL_MODELVIEW);
}


void computePos(float deltaMove) {

	x += deltaMove * lx * 0.1f;
	z += deltaMove * lz * 0.1f;
}

void computePos2(float deltaMove2)
{
    float aux1 = (float) asin(-1*lx);
    float aux2 = (float) acos(-1*lz);
    //printf("\ndiv: %f",div);
    //div = div/180;

    float lx2 = cos(aux1+M_PI);
    float lz2 = sin(aux2);

    x += deltaMove2 * lx2 * 0.1f;
	z += deltaMove2 * lz2 * 0.1f;
}

void saveJPG(int width, int height, int iout)
{
    char fileName[50];

    sprintf(fileName,"%03d.jpg",iout-1);

    unsigned char *pdata = (unsigned char*) malloc(width*height*3);
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pdata);

    FILE *outfile;
    if ((outfile = fopen(fileName, "wb")) == NULL) {
        printf("can't create %s",fileName);
        exit(1);
      }

    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr       jerr;

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);
    jpeg_stdio_dest(&cinfo, outfile);

    cinfo.image_width      = width;
    cinfo.image_height     = height;
    cinfo.input_components = 3;
    cinfo.in_color_space   = JCS_RGB;

    jpeg_set_defaults(&cinfo);
    /*set the quality [0..100]  */
    jpeg_set_quality (&cinfo, 100, 1);
    jpeg_start_compress(&cinfo, 1);

    JSAMPROW row_pointer;
    int row_stride = width * 3;

    while (cinfo.next_scanline < cinfo.image_height)
	{
		row_pointer = (JSAMPROW) &pdata[(cinfo.image_height-1-cinfo.next_scanline)*row_stride];
		jpeg_write_scanlines(&cinfo, &row_pointer, 1);
	}

    jpeg_finish_compress(&cinfo);

    fclose(outfile);

    jpeg_destroy_compress(&cinfo);
}


void saveBMP( int Width, int Height, int iout)
{
    BITMAPFILEHEADER bf;
    BITMAPINFOHEADER bi;

    char fileName[50];

    sprintf(fileName,"%03d.bmp",iout-1);

    unsigned char *image = (unsigned char*)malloc(sizeof(unsigned char)*Width*Height*3);
    FILE *file = fopen(fileName, "wb");

    if( image!=NULL )
    {
        if( file!=NULL )
        {
            glReadPixels( 0, 0, Width, Height, GL_BGR_EXT, GL_UNSIGNED_BYTE, image );

            memset( &bf, 0, sizeof( bf ) );
            memset( &bi, 0, sizeof( bi ) );

            bf.bfType = 0x4D42; //"MB"
            bf.bfSize = sizeof(bf)+sizeof(bi)+Width*Height*3;
            bf.bfOffBits = sizeof(bf)+sizeof(bi);
            bi.biSize = sizeof(bi);
            bi.biWidth = Width;
            bi.biHeight = Height;
            bi.biPlanes = 1;
            bi.biBitCount = 24;
            bi.biSizeImage = Width*Height*3;

            fwrite( &bf, sizeof(bf), 1, file );
            fwrite( &bi, sizeof(bi), 1, file );
            fwrite( image, sizeof(unsigned char), Height*Width*3, file );

            fclose( file );
        }
        free( image );
    }
}

void genImg(int width, int height)
{
	char ResultFileName[50];
	
	sprintf(ResultFileName, "%03d.dat", iout++);
	GenerateResultFile(ResultFileName);
	
	if (!strcmp(img_fmt,"bmp"))
		saveBMP(width,height,iout);
	if (!strcmp(img_fmt,"jpg"))
		saveJPG(width,height,iout);
}

void renderScene(void) {

    int width = glutGet(GLUT_WINDOW_WIDTH);
    int height = glutGet(GLUT_WINDOW_HEIGHT);

    char ResultFileName[50];

	if (deltaMove)
		computePos(deltaMove);

    if (deltaMove2)
        computePos2(deltaMove2);
        
    if (animate != 1)
    {
		t += CalculaNCorpos();
	

		if (t>tout)
		{
			tout = t + dtout;
			
			genImg(width,height);

			/*
			sprintf(ResultFileName, "%03d.dat", iout++);
			GenerateResultFile(ResultFileName);

			if (!strcmp(img_fmt,"bmp"))
				saveBMP(width,height,iout);
			if (!strcmp(img_fmt,"jpg"))
				saveJPG(width,height,iout);
				* */
		}
	}
	
	else
	{	
		sprintf(ResultFileName, "%03d.dat", iout);
		
		if (iout == 0)
		{
			n = getNparticules(ResultFileName);
			ArrayHead = calloc(n,sizeof(particuleData));
		}
		
		if(LoadFile(ResultFileName, ArrayHead))
			genImg(width,height);
		else
			exit(1);
		
	}

	// Clear Color and Depth Buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Reset transformations
	glLoadIdentity();
	// Set the camera
	gluLookAt(	x, z, 0.0f,
			x+lx, z+lz,  0.0f,
			0.0f, 0.0f,  1.0f);

// Draw ground
/*
	glColor3f(0.9f, 0.9f, 0.9f);
	glBegin(GL_QUADS);
		glVertex3f(-100.0f, 0.0f, -100.0f);
		glVertex3f(-100.0f, 0.0f,  100.0f);
		glVertex3f( 100.0f, 0.0f,  100.0f);
		glVertex3f( 100.0f, 0.0f, -100.0f);
	glEnd();
*/

// Draw 36 SnowMen

    drawParticle();
//    int i,j;
/*
	for(i = -3; i < 3; i++)
		for(j=-3; j < 3; j++) {
			glPushMatrix();
			glTranslatef(i*10.0f, 0.0f, j * 10.0f);
			drawSnowMan();
			glPopMatrix();
		}
*/
    // Code to compute frames per second
	frame++;

	time=glutGet(GLUT_ELAPSED_TIME);
	if (time - timebase > 1000) {
		sprintf(s,"FPS:%4.2f",
			frame*1000.0/(time-timebase));
		timebase = time;
		frame = 0;
	}

        // Code to display a string (fps) with bitmap fonts
	setOrthographicProjection();

	glPushMatrix();
	glLoadIdentity();
	renderBitmapString(5,30,0,GLUT_BITMAP_HELVETICA_18,s);
	glPopMatrix();

	restorePerspectiveProjection();


	glutSwapBuffers();
}

// -----------------------------------
//             KEYBOARD
// -----------------------------------

void processNormalKeys(unsigned char key, int xx, int yy) {

	glutSetMenu(mainMenu);
	switch (key) {
		case 27:
			glutDestroyMenu(mainMenu);
			glutDestroyMenu(zoomMenu);
			exit(0);
			break;

        case 'w': deltaMove = 0.1f; break;
        case 's': deltaMove = -0.1f; break;
        case 'a': deltaMove2 = -0.1f; break;
        case 'd': deltaMove2 = 0.1f; break;

	}
	if (key == 27)
		exit(0);
}

void releaseNormalKeys(unsigned char key, int xx, int yy)
{
    switch (key)
    {
        case 'w':
        case 's': deltaMove=0; break;
        case 'a':
        case 'd': deltaMove2=0; break;
    }
}

void pressKey(int key, int xx, int yy) {

	switch (key) {
		case GLUT_KEY_UP : deltaMove = 0.5f; break;
		case GLUT_KEY_DOWN : deltaMove = -0.5f; break;
	}
}

void releaseKey(int key, int x, int y) {

	switch (key) {
		case GLUT_KEY_UP :
		case GLUT_KEY_DOWN : deltaMove = 0;break;
	}
}

// -----------------------------------
//             MOUSE
// -----------------------------------

void mouseScreen(int state)
{

    if (state == GLUT_ENTERED)
    {
        screen = 1;
    }

    if (state == GLUT_LEFT)
    {
        screen = 0;
    }
}

void mouseMove(int x, int y)
{

    if (screen == 1)
    {


        // update deltaAngle
        float dx = (float)(x - centerX)*2 /WINDOWSIZEX;
        float dz = (float)(y - centerY)*2 /WINDOWSIZEY;

        // update camera's direction
        lx = -sin(angle + dx);
        lz = -cos(angle + dx);

        //printf("\nlx: %f, lz: %f",lx, lz);

    }

}

void mouseButton(int button, int state, int x, int y) {

	// only start motion if the left button is pressed
	if (button == GLUT_LEFT_BUTTON) {

		// when the button is released
		if (state == GLUT_UP) {
			angle += deltaAngle;
			xOrigin = -1;
		}
		else  {// state = GLUT_DOWN
			xOrigin = x;
		}
	}
}

// -----------------------------------
//             MENUS
// -----------------------------------

void processMenuStatus(int status, int x, int y) {

	if (status == GLUT_MENU_IN_USE)
		menuFlag = 1;
	else
		menuFlag = 0;
}

void processMainMenu(int option) {

	// nothing to do in here
	// all actions are for submenus
}


void processZoomMenu(int option) {

	switch (option) {

		case ZIN: scale += 0.25f; break;
		case ZOUT: scale -= 0.25f; break;
	}
}

void processColorMenu(int option)
{
    switch (option)
    {
        case 1: red = 1.0f; green = 1.0f; blue = 1.0f; break;
        case 2: red = 1.0f; green = 0.0f; blue = 0.0f; break;
        case 3: red = 1.0f; green = 1.0f; blue = 0.0f; break;
    }
}

void createPopupMenus() {


	zoomMenu = glutCreateMenu(processZoomMenu);

	glutAddMenuEntry("Zoom In",ZIN);
	glutAddMenuEntry("Zoom Out",ZOUT);

	colorMenu = glutCreateMenu(processColorMenu);

	glutAddMenuEntry("White",1);
	glutAddMenuEntry("Red",2);
	glutAddMenuEntry("Yellow",3);


	mainMenu = glutCreateMenu(processMainMenu);

	glutAddSubMenu("Zoom", zoomMenu);
	glutAddSubMenu("Color", colorMenu);
	// attach the menu to the right button
	glutAttachMenu(GLUT_RIGHT_BUTTON);

	// this will allow us to know if the menu is active
	glutMenuStatusFunc(processMenuStatus);
}
// END OF GLUT FUNCTIONS

// SIMULATOR FUNCTIONS
int FileExists (char *FileName)
{
	FILE *file_p;

    printf("... trying to check the file %s...\n", FileName);
	file_p = fopen(FileName, "r");
	if(file_p)
	{
		fclose(file_p);
		printf("... file '%s' found!!!!...\n", FileName);
		return(TRUE);
	}
	return(FALSE);
}

particuleData LineParsing(char *LineBuffer)
{
	particuleData Element;

	int  Cont      = 0;
	int  PosAux    = 0;
	int  Position  = 0;
	int  LineSize;
	char BufferAux[500];

	LineSize = strlen(LineBuffer);

	printf("==> Line being processed: %s\n", LineBuffer);

	// parse the line, finding the fields separated by blank spaces " " until reach the end of the line.
	while (Position <= LineSize)
	{

		if(LineBuffer[Position] != ' ') // consume the line, character by character.
		{
			BufferAux[Position-PosAux] = LineBuffer[Position];
		}
		if((LineBuffer[Position] == ' ' ) || (Position==LineSize)) // field separator or end of the line???
		{
			BufferAux[Position-PosAux] = '\0';
			switch (++Cont)
			{
				case 1: Element.PosX = atof(BufferAux); break;
				case 2: Element.PosY = atof(BufferAux); break;
				case 3: Element.PosZ = atof(BufferAux); break;
				case 4: Element.vX   = atof(BufferAux); break;
				case 5: Element.vY   = atof(BufferAux); break;
				case 6: Element.vZ   = atof(BufferAux); break;
				case 7: Element.Mass = atof(BufferAux); Cont=0; break;
			}
			PosAux = Position+1;
		}
		Position++;
	}
	return Element;
}

void GenerateResultFile(char* FileName)
{

    //
    // used to generated the data file containing the updated particles positions.
    //

    FILE *file_p;
    int i;

    file_p = fopen(FileName, "w");

    for(i = 0; i < n; i++)
    {
        fprintf(file_p, "%f %f %f %f %f %f %f\n", ArrayHead[i].PosX, ArrayHead[i].PosY, ArrayHead[i].PosZ,
		                                          ArrayHead[i].vX, ArrayHead[i].vY, ArrayHead[i].vZ,
												  ArrayHead[i].Mass);
		//fprintf(file_p, "%f %f %f\n", Node->Content.PosX, Node->Content.PosY, Node->Content.PosZ);
        //fprintf(file_p, "%d %d %d\n", Node->Content.PosX, Node->Content.PosY, Node->Content.PosZ);
    }

    fclose(file_p);

}

void EnergyLog(char* FileName, double time, double energy, double energyerr, char* fmode)
{

    //
    // used to generated the Energy Log.
    //

    FILE *file_p;

    file_p = fopen(FileName, fmode);

	fprintf(file_p, "%e %e %e\n", time, energy, energyerr);

    fclose(file_p);

}


int LoadFile(char *FileName, particuleData *ArrayHead)
{

    //
    // Load the file containing the initial conditions.
    //

	FILE *file_p;
	char  LineBuffer[100];
	int   Contador;

	printf("\nOpening file %s... \n", FileName);

	if(!FileExists(FileName))
	{
		printf("\n File %s doesn't exists!!! \n", FileName);
		return(FALSE);
	}

	file_p = fopen(FileName, "r");
	if(!file_p)
	{
		printf("\nError opening data file !\n");
		return(FALSE);
	}

	Contador=0;
	while( fgets(LineBuffer, 100, file_p) != NULL )
	{
        particuleData P;
        P = LineParsing(LineBuffer);
        P.ID = Contador;
        ArrayHead[Contador] = P;
        Contador++;


	};

	fclose(file_p);

	printf("\n %s was closed successfully!\n", FileName);

	return(TRUE);
};

void CalculateForce(particuleData* PartBase, particuleData* PartNext, int n, double eps)
{

    //
    // Calculate the force between two particules (PartBase and PartNext)
    //

    //double Force; //unused variable!
    double XDist;
    double YDist;
    double ZDist;
    double XT;
    double YT;
    double ZT;
    double DistanceSQR;
    double Rinv;
    double Rinv3;

    // WARNING!!! Don't forget to update this line with the amount of particules to be
    // processed! eps = 4/n  !!!! Use decimal point! eps is the softening factor!
    //double eps = 4.0/ 395.0;
	//double eps = 0.0;

    XDist = PartBase->PosX - PartNext->PosX;
    XT = XDist * XDist;

    YDist = PartBase->PosY - PartNext->PosY;
    YT = YDist * YDist;

    ZDist = PartBase->PosZ - PartNext->PosZ;
    ZT = ZDist * ZDist;

    DistanceSQR = XT + YT + ZT;

	Rinv = 1.0 / sqrt(DistanceSQR + (eps*eps));

    Rinv3 = Rinv * Rinv * Rinv;


    PartBase->fX -= PartNext->Mass * XDist * Rinv3;
    PartBase->fY -= PartNext->Mass * YDist * Rinv3;
    PartBase->fZ -= PartNext->Mass * ZDist * Rinv3;

}

double CalculaNCorpos()
{

    //
    //  The particles are saved in a linked list, generated from the load of the initial conditions file.
    //  This function scans the whole linked list, calculating the gravitational attraction between each particle
    // with each other, one by one. This way, we will have N^2 interactions.
    //  After comparing all particles, finally update the positions and speeds.
    //  It's a time stepped algorithm, so each linked list scan represents a step in time, whose evolution value is
    // given by the 'dt' parameter. It's MANDATORY to keep this value fewer than '1'.
    //

    int i,j;


    for(i = 0; i < n; i++)
    {
        // updating the coordinates
        ArrayHead[i].PosX += (ArrayHead[i].vX * dt)/2;
        ArrayHead[i].PosY += (ArrayHead[i].vY * dt)/2;
        ArrayHead[i].PosZ += (ArrayHead[i].vZ * dt)/2;
    }


    for(i = 0; i < n; i++)
    {
        ArrayHead[i].fX = 0.0;
        ArrayHead[i].fY = 0.0;
        ArrayHead[i].fZ = 0.0;

        for(j = 0; j < n; j++)
            if(ArrayHead[i].ID != ArrayHead[j].ID) // we don't have to calculate the node against itself, right?
            {
                    CalculateForce(&(ArrayHead[i]), &(ArrayHead[j]), n, eps);
            }
    }


    for(i = 0; i < n; i++)
    {

        // updating the velocities
        ArrayHead[i].vX   += ArrayHead[i].fX * dt;
        ArrayHead[i].vY   += ArrayHead[i].fY * dt;
        ArrayHead[i].vZ   += ArrayHead[i].fZ * dt;

        // updating the coordinates
        ArrayHead[i].PosX += (ArrayHead[i].vX * dt)/2;
        ArrayHead[i].PosY += (ArrayHead[i].vY * dt)/2;
        ArrayHead[i].PosZ += (ArrayHead[i].vZ * dt)/2;

    }

    return dt;

}

int getNparticules(char *FileName)
{
	FILE* file_p;
	char LineBuffer[100];
	int cont=0;
	int i;

	file_p=fopen(FileName,"r");

	while( fgets(LineBuffer, 100, file_p) != NULL )
	{
		for (i=0;i<100;i++)
			{
				if (isdigit(LineBuffer[i]))
				{
					cont++;
					break;
				}
			}
	}

	fclose(file_p);

	return cont;
}

// -----------------------------------
//             MAIN
// -----------------------------------

int main(int argc, char **argv) {

    char InitialConditionsFileName[50];
    char strTrace[10];
    int trace;

    if (argc>=2)
    {
		int argcont;

		for(argcont=0;argcont<argc;argcont++)
		{

			if (!strcmp(argv[argcont],"-init"))
			{
				strcpy(InitialConditionsFileName,argv[argcont+1]);
			}

			if (!strcmp(argv[argcont],"-trace"))
			{
				strcpy(strTrace,argv[argcont+1]);
			}
			
			if (!strcmp(argv[argcont],"-img"))
			{
				if ((strlen(argv[argcont]) == 4) && (argv[argcont][4]=='\0'))
					strcpy(img_fmt,argv[argcont+1]);
				else
				{
					printf("\ninvalid image parameters, type 3 characters of image format");
					exit(1);
				}
			}
			
			if (!strcmp(argv[argcont],"-animate"))
			{
				animate = 1;
			}
			
		}
	}

	else
	{
		//printf("\n:::Initial file not given, please start the program using -init filename.ini:::\n\n");
		//exit(1);
	}
	
	if (animate != 1)
	{
		n = getNparticules(InitialConditionsFileName);
		
		// softening factor
		if(n>100)eps = 4.0/n;

		//printf("\n======= %d found on initial file =======\n",n);
		ArrayHead = (particuleData*) calloc (n,sizeof(particuleData));
		if (ArrayHead == NULL)
		{
			printf("Function failed to allocate the requested block of memory, closing program...\n");
			exit(1);
		}

		//char NroArq[] = "  "; //unused variable!
		LoadFile(InitialConditionsFileName, ArrayHead);
	}
	
	//animate == 1
	else
	{
		if ((strlen(img_fmt)!=3))
		{
			printf("\nInvalid image format. Type a 3 characters image format using -img XXX\n");
			exit(1);
		}
	}
	
    //--------------------
    //      GLUT STUFF
    //--------------------

	// init GLUT and create window
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100,100);
	glutInitWindowSize(WINDOWSIZEX,WINDOWSIZEY);

	glutCreateWindow("Sagan Simulator");

    glutFullScreenToggle();

	centerX = WINDOWSIZEX/2;
	centerY = WINDOWSIZEY/2;

	// register callbacks
	glutDisplayFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutIdleFunc(renderScene);

	glutIgnoreKeyRepeat(1);
	glutKeyboardFunc(processNormalKeys);
	glutKeyboardUpFunc(releaseNormalKeys);
	glutSpecialFunc(pressKey);
	glutSpecialUpFunc(releaseKey);

	// here are the two new functions
	glutEntryFunc(mouseScreen);
	glutMouseFunc(mouseButton);
	glutPassiveMotionFunc(mouseMove);

	// OpenGL init
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);

	// init Menus
	createPopupMenus();

	// enter GLUT event processing cycle
	glutMainLoop();

	return 1;
}
